package com.project.oauth2Server.controller;


import com.project.annotation.enums.common.SuccessBaseCode;
import com.project.annotation.model.UserConfig;
import com.project.annotation.model.base.ResultBase;
import com.project.annotation.model.base.VoidResult;
import com.project.oauth2Server.Authorization.AuthServerConfiguration;
import com.project.oauth2Server.common.enums.*;
import com.project.oauth2Server.config.AuthConfig;
import com.project.oauth2Server.config.ClientConfig;
import com.project.oauth2Server.config.redis.AuthInfoRedis;
import com.project.oauth2Server.config.redis.CodeInfoRedis;
import com.project.oauth2Server.config.redis.RefreshInfoRedis;
import com.project.oauth2Server.config.redis.UserInfoRedis;
import com.project.oauth2Server.controller.business.OAuth2CtrlInterface;
import com.project.oauth2Server.model.AccessTokenInfo;
import com.project.oauth2Server.model.TokenInfo;
import com.project.thisCommon.common.CommonFunction;
import com.project.thisCommon.common.ConvertEntity;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.apache.http.message.BasicNameValuePair;
import org.eclipse.jetty.util.URIUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.web.bind.annotation.*;

import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.Principal;
import java.util.*;

import static org.springframework.http.HttpHeaders.encodeBasicAuth;

//2.0修改
@RestController
//@EnableGlobalMethodSecurity(prePostEnabled = true)//打开权限验证
public class OAuth2Consume implements OAuth2CtrlInterface {

    @Autowired
    ConsumerTokenServices tokenServices;

    @Autowired
    AuthConfig authConfig;

    @Autowired
    AuthInfoRedis authInfoRedis;

    @Autowired
    UserInfoRedis userInfoRedis;

    @Autowired
    CodeInfoRedis codeInfoRedis;

    @Autowired
    RefreshInfoRedis refreshInfoRedis;

    @Autowired(required = false)
    AuthServerConfiguration authServerConfiguration;

    @Override
    @RolesAllowed({"EDITOR"})
    public void registerClient(ClientConfig config) {
        authServerConfiguration.bindClient(config);
    }

    @Override
    public ResultBase<AccessTokenInfo> refreshToken(
            HttpServletRequest request, HttpServletResponse response,
            String client_id,
            String client_secret,
            String refresh_token) throws Exception {

        String access_token = refreshInfoRedis.getExecutor().getByKey(refresh_token);
        String name = authInfoRedis.getExecutor().getByKey(access_token);

        List<BasicNameValuePair> mp = new ArrayList<>();
        mp.add(new BasicNameValuePair("grant_type", "refresh_token"));
        mp.add(new BasicNameValuePair("refresh_token", refresh_token));

        List<BasicNameValuePair> hd = new ArrayList<>();
        hd.add(new BasicNameValuePair("Authorization", "Basic " + encodeBasicAuth(client_id, client_secret, null)));
        StringBuilder url = new StringBuilder();
        URIUtil.appendSchemeHostPort(url, request.getScheme(), request.getServerName(), request.getServerPort());
        String token_url = url + "/oauth/token";
        String json = CommonFunction.RunUrl(token_url, mp, hd, "utf-8");
        AccessTokenInfo info = ConvertEntity.jsonToJava(json, AccessTokenInfo.class);

        authInfoRedis.getExecutor().deleteByKey(access_token);
        refreshInfoRedis.getExecutor().deleteByKey(refresh_token);
        authInfoRedis.getExecutor().addString(info.getAccess_token(), name);
        refreshInfoRedis.getExecutor().addString(info.getRefresh_token(), info.getAccess_token());

        return ResultBase.ok(info, SuccessBaseCode.SYSTEM_SUCCESS);
    }

    @Override
    public ResultBase logout(String token) {
        tokenServices.revokeToken(token);
        authInfoRedis.getExecutor().deleteByKey(token);
        return ResultBase.ok(SuccessBaseCode.SYSTEM_SUCCESS);
    }

    @SneakyThrows
    @Override
    public ResultBase<AccessTokenInfo> login(
            HttpServletRequest request, HttpServletResponse response,
            String code,
            String client_id,
            String client_secret,
            String redirect_uri) {

        List<BasicNameValuePair> mp = new ArrayList<>();
        mp.add(new BasicNameValuePair("code", code));
        mp.add(new BasicNameValuePair("client_id", client_id));
        mp.add(new BasicNameValuePair("grant_type", "authorization_code"));
        mp.add(new BasicNameValuePair("redirect_uri", redirect_uri));
        mp.add(new BasicNameValuePair("client_secret", client_secret));

        List<BasicNameValuePair> hd = new ArrayList<>();
        hd.add(new BasicNameValuePair("Authorization", "Basic " + encodeBasicAuth(client_id, client_secret, null)));
        StringBuilder url = new StringBuilder();
        URIUtil.appendSchemeHostPort(url, request.getScheme(), request.getServerName(), request.getServerPort());
        String token_url = url + "/oauth/token";
        String json = CommonFunction.RunUrl(token_url, mp, hd, "utf-8");
        AccessTokenInfo info = ConvertEntity.jsonToJava(json, AccessTokenInfo.class);
        String name = codeInfoRedis.getExecutor().getByKey(code);
        authInfoRedis.getExecutor().addString(info.getAccess_token(), name);
        refreshInfoRedis.getExecutor().addString(info.getRefresh_token(), info.getAccess_token());

        return ResultBase.ok(info, SuccessBaseCode.SYSTEM_SUCCESS);
    }

    @Override
    @RolesAllowed({"ADMIN"})
    public ResultBase<TokenInfo> info(String token) {
        String json = authInfoRedis.getExecutor().getByKey(token);

        if (StringUtils.isNotEmpty(json)) {
            TokenInfo tokenInfo = ConvertEntity.jsonToJava(json, TokenInfo.class);
            return ResultBase.ok(tokenInfo, SuccessBaseCode.SYSTEM_SUCCESS);
        }

        return ResultBase.ok(null, ExceptionCode.AUTHORITY_ERROR);
    }

    @Override
    @RolesAllowed({"ADMIN"})
    public ResultBase<VoidResult> createUserInfo(UserConfig item) {
        userInfoRedis.getExecutor().addMap(item.getUserName(), item);
        return ResultBase.void_ok(SuccessBaseCode.SYSTEM_SUCCESS);
    }

    @Override
    public ResultBase<Boolean> check(String token) {
        String json = authInfoRedis.getExecutor().getByKey(token);
        return ResultBase.ok(StringUtils.isNotEmpty(json), SuccessBaseCode.SYSTEM_SUCCESS);
    }

    @Override
    public ResultBase<String> callback(String token, Principal principal) {
        codeInfoRedis.getExecutor().addString(token, principal.getName());
        return ResultBase.ok(token, SuccessBaseCode.SYSTEM_SUCCESS);
    }
}
